/*
@title: bannai-snake
@description: "Bannai Snake" is a puzzle game where players navigate through a grid, solving challenges by creating valid paths. The game emphasizes avoiding diagonal connections and keeping paths one tile thick, with various levels guiding players through these mechanics.
@author: aagrim hoysal
@tags: ['puzzle']
@addedOn: 2023-02-13
*/

const player = "p"
const filled = "0";
const unfilled = "1";
const wall = "w";
const goal = "g";
const menu = "m";
const error = "e";

const songs = [ tune`
468.75: f4^468.75,
468.75: a4^468.75,
468.75: a4^468.75,
468.75: e4^468.75,
468.75: a4^468.75,
468.75: a4^468.75,
468.75: d4^468.75,
468.75: a4^468.75,
468.75: a4^468.75,
468.75: c4^468.75,
468.75: c5~468.75 + c4^468.75,
468.75: e5~468.75 + e4^468.75,
468.75: f5~468.75,
468.75: a5~468.75,
468.75: a5~468.75,
468.75: e5~468.75,
468.75: a5~468.75,
468.75: a5~468.75,
468.75: d5~468.75,
468.75: a5~468.75,
468.75: a5~468.75,
468.75: c5~468.75,
468.75: d5~468.75,
468.75: e5~468.75,
468.75: f5~468.75 + f4~468.75,
468.75: a4~468.75,
468.75: c5~468.75 + f4~468.75,
468.75: a4~468.75,
468.75: f5~468.75 + f4~468.75,
468.75: a4~468.75,
468.75: a4~468.75 + f4~468.75,
468.75: a4~468.75`, tune`
329.6703296703297: f4^329.6703296703297,
329.6703296703297: a4^329.6703296703297,
329.6703296703297: a4^329.6703296703297,
329.6703296703297: e4^329.6703296703297,
329.6703296703297: a4^329.6703296703297,
329.6703296703297: a4^329.6703296703297,
329.6703296703297: d4^329.6703296703297,
329.6703296703297: a4^329.6703296703297,
329.6703296703297: a4^329.6703296703297,
329.6703296703297: c4^329.6703296703297,
329.6703296703297: c5~329.6703296703297 + c4^329.6703296703297,
329.6703296703297: e5~329.6703296703297 + e4^329.6703296703297,
329.6703296703297: f5~329.6703296703297 + f4^329.6703296703297,
329.6703296703297: a5~329.6703296703297,
329.6703296703297: a5~329.6703296703297,
329.6703296703297: e5~329.6703296703297 + e4^329.6703296703297,
329.6703296703297: a5~329.6703296703297,
329.6703296703297: a5~329.6703296703297,
329.6703296703297: d5~329.6703296703297 + d4^329.6703296703297,
329.6703296703297: a5~329.6703296703297,
329.6703296703297: a5~329.6703296703297,
329.6703296703297: c5~329.6703296703297 + c4^329.6703296703297,
329.6703296703297: d5~329.6703296703297,
329.6703296703297: e5^329.6703296703297 + e4~329.6703296703297,
329.6703296703297: f5^329.6703296703297 + f4~329.6703296703297,
329.6703296703297: a4~329.6703296703297,
329.6703296703297: c5^329.6703296703297 + f4~329.6703296703297,
329.6703296703297: a4~329.6703296703297,
329.6703296703297: f5^329.6703296703297 + f4~329.6703296703297,
329.6703296703297: a4~329.6703296703297,
329.6703296703297: f4~329.6703296703297 + a4^329.6703296703297,
329.6703296703297: a4~329.6703296703297`, tune`
333.3333333333333: f4^333.3333333333333 + f5/333.3333333333333,
333.3333333333333: a4^333.3333333333333,
333.3333333333333: a4^333.3333333333333 + d5/333.3333333333333,
333.3333333333333: e4^333.3333333333333 + g5/333.3333333333333,
333.3333333333333: a4^333.3333333333333,
333.3333333333333: a4^333.3333333333333 + a5/333.3333333333333,
333.3333333333333: d4^333.3333333333333 + f5/333.3333333333333,
333.3333333333333: a4^333.3333333333333,
333.3333333333333: a4^333.3333333333333,
333.3333333333333: c4^333.3333333333333 + g4~333.3333333333333,
333.3333333333333: c5~333.3333333333333 + c4^333.3333333333333,
333.3333333333333: e5~333.3333333333333 + f4^333.3333333333333,
333.3333333333333: f5~333.3333333333333 + f4/333.3333333333333,
333.3333333333333: a5~333.3333333333333,
333.3333333333333: a5~333.3333333333333 + a4/333.3333333333333,
333.3333333333333: e5~333.3333333333333 + e4/333.3333333333333,
333.3333333333333: a5~333.3333333333333,
333.3333333333333: a5~333.3333333333333 + a4/333.3333333333333,
333.3333333333333: d5~333.3333333333333 + c5/333.3333333333333 + d4^333.3333333333333,
333.3333333333333: a5~333.3333333333333,
333.3333333333333: a5~333.3333333333333,
333.3333333333333: c5~333.3333333333333 + c4^333.3333333333333,
333.3333333333333: d5~333.3333333333333,
333.3333333333333: e5^333.3333333333333 + e4~333.3333333333333,
333.3333333333333: f5^333.3333333333333 + f4~333.3333333333333 + d5/333.3333333333333,
333.3333333333333: a4~333.3333333333333,
333.3333333333333: c5^333.3333333333333 + f4~333.3333333333333 + d5/333.3333333333333,
333.3333333333333: a4~333.3333333333333 + c5/333.3333333333333,
333.3333333333333: f5^333.3333333333333 + f4~333.3333333333333 + a4/333.3333333333333,
333.3333333333333: a4~333.3333333333333 + c5/333.3333333333333,
333.3333333333333: f4~333.3333333333333 + a4^333.3333333333333 + f5/333.3333333333333,
333.3333333333333: a4~333.3333333333333`, tune`
258.62068965517244: f4^258.62068965517244 + f5/258.62068965517244 + c5~258.62068965517244,
258.62068965517244: a4^258.62068965517244 + d5~258.62068965517244,
258.62068965517244: a4^258.62068965517244 + d5/258.62068965517244,
258.62068965517244: e4^258.62068965517244 + g5/258.62068965517244 + b4~258.62068965517244,
258.62068965517244: a4^258.62068965517244 + d5~258.62068965517244,
258.62068965517244: a4^258.62068965517244 + a5/258.62068965517244 + d5~258.62068965517244,
258.62068965517244: d4^258.62068965517244 + f5/258.62068965517244 + a4~258.62068965517244,
258.62068965517244: a4^258.62068965517244 + d5~258.62068965517244,
258.62068965517244: a4^258.62068965517244 + d5~258.62068965517244,
258.62068965517244: c4^258.62068965517244 + g4~258.62068965517244,
258.62068965517244: c5~258.62068965517244 + c4^258.62068965517244,
258.62068965517244: e5~258.62068965517244 + e4^258.62068965517244,
258.62068965517244: f5~258.62068965517244 + f4/258.62068965517244,
258.62068965517244: a5~258.62068965517244 + a4^258.62068965517244,
258.62068965517244: a5~258.62068965517244 + a4/258.62068965517244,
258.62068965517244: e5~258.62068965517244 + e4/258.62068965517244,
258.62068965517244: a5~258.62068965517244 + a4^258.62068965517244,
258.62068965517244: a5~258.62068965517244 + a4/258.62068965517244,
258.62068965517244: d5~258.62068965517244 + c5/258.62068965517244 + d4^258.62068965517244,
258.62068965517244: a5~258.62068965517244 + a4^258.62068965517244,
258.62068965517244: a5~258.62068965517244 + a4^258.62068965517244,
258.62068965517244: c5~258.62068965517244 + c4^258.62068965517244,
258.62068965517244: d5~258.62068965517244,
258.62068965517244: e5^258.62068965517244 + e4~258.62068965517244,
258.62068965517244: f5^258.62068965517244 + f4~258.62068965517244 + d5/258.62068965517244,
258.62068965517244: a4~258.62068965517244,
258.62068965517244: c5^258.62068965517244 + f4~258.62068965517244 + d5/258.62068965517244,
258.62068965517244: a4~258.62068965517244 + c5/258.62068965517244,
258.62068965517244: f5^258.62068965517244 + f4~258.62068965517244 + a4/258.62068965517244,
258.62068965517244: a4~258.62068965517244 + c5/258.62068965517244,
258.62068965517244: f4~258.62068965517244 + a4^258.62068965517244 + f5/258.62068965517244,
258.62068965517244: a4~258.62068965517244` ]

setLegend(
  [ wall, bitmap`
6666666666666666
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6.............66
6666666666666666
6666666666666666` ],
  [ error, bitmap`
3333333333333333
33............33
3.3..........3.3
3..3........3..3
3...3......3...3
3....3....3....3
3.....3..3.....3
3......33......3
3......33......3
3.....3..3.....3
3....3....3....3
3...3......3...3
3..3........3..3
3.3..........3.3
33............33
3333333333333333` ],
  
  [ player, bitmap`
2222222222222222
2222222222222222
2292929992929292
2292929292929292
2292929292929292
2292929292929292
2299929292929292
2222929292929292
2292929292929292
2222929292929292
2292929292929292
2292929292929292
2292929292929222
2299929992999292
2222222222222222
2222222222222222` ],
  [ filled, bitmap`
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222
2222222222222222` ],
  [ unfilled, bitmap`
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000` ],
  
  [ menu, bitmap`
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHH` ],
  [ goal, bitmap`
0000000000000000
0000000000000000
0000000000000000
0000000660000000
0000000660000000
0000006666000000
0006666666666000
0000666666660000
0000006666000000
0000066666600000
0000666006660000
0000660000660000
0000000000000000
0000000000000000
0000000000000000
0000000000000000` ]
)


setSolids([player, wall, menu])

const tutorial = 3;

// Music
let track = 0;
let playback = playTune(songs[0], Infinity);

let reset = false;
let level = 0;
let levelData = {};
const levels = [
  // Title screen
  {
    "map" : map`
mmmmmmmmmm
m11111111m
m11111111m
m11111111m
m11111111m
m11111111m
m11111111m
mmmmmmmmmm`,
    "title" : [
      [6, "BANNAI SNAKE"],
      [9, "press j"],
      [10, "to advance"]
    ],
    "music" : 0
  },
  // Line tutorial
  {
    "map" : map`
mmmmmmmmmm
mmmmmmmmmm
0000000111
1111010000
0000000110
1001111111
mmmmmmmmmm
mmmmmmmmmm`,
    "title" : [
      [1, "all paths must"],
      [2, "be one tile thick"],
      [13, "to be valid"],
      [14, "(black paths too!)"],
    ],
    "add" : [
      [1, 5, error],
      [1, 4, error],
      [2, 5, error],
      [2, 4, error],
      [8, 4, error],
      [7, 4, error],
      [8, 5, error],
      [7, 5, error],
    ],
    "music" : 0
  },
  // Diagonals tutorial
  {
    "map" : map`
mmmmmmmmmm
mmmmmmmmmm
0000010000
1011010101
1010101101
0000100000
mmmmmmmmmm
mmmmmmmmmm`,
    "title" : [
      [1, "lines can't"],
      [2, "touch diagonally"],
      [13, "but can touch"],
      [14, "at a right angle"]
    ],
    "add" : [
      [3, 4, error],
      [4, 3, error],
      [5, 4, error],
      [6, 3, error]
    ],
    "music" : 0
  },
  // Controls tutorial
  {
    "map" : map`
mmmmmmmmmm
mmmmmmmmmm
mmmmmmmmmm
mmmm01mmmm
mmmmp0mmmm
mmmmmmmmmm
mmmmmmmmmm
mmmmmmmmmm`,
    "title" : [
      [1, "you can double back"],
      [2, "from where you came"],
      [3, "(you can move up"],
      [4, "or right from here)"],
      [10, "the entire board"],
      [11, "must follow the"],
      [12, "previous two rules"],
      [13, "wasd to move"],
      [14, "j to reset"]
    ],
    "music" : 0
  },
  // Blank Canvas (L1)
  {
    "map" : map`
1111
1111
1111
111g
mmmm`,
    "add" : [ [0, 0, player] ],
    "title" : [
      [14, "blank canvas"]
    ],
    "text" : [
      [13, "remember you"],
      [14, "can double back"],
      [15, "on yourself!"],
    ],
    "music" : 0
  },
  // Solid Gold (L2)
  {
    "map" : map`
11111
11111
11111
01101
1111g
mmmmm`,
    "add" : [ 
      [0, 0, player], 
      [0, 3, wall], 
      [1, 3, wall], 
      [3, 3, wall]
    ],
    "title" : [
      [14, "solid gold"]
    ],
    "text" : [
      [14, "sorry, try"],
      [15, "again!"],
    ],
    "music" : 1
  },
  // Get Forked (L3)
  {
    "map" : map`
101101
11111g
111110
111111
111110
mmmmmm`,
    "add" : [ 
      [0, 0, player],
      [1, 0, wall], 
      [4, 0, wall], 
      [2, 1, wall], 
      [1, 2, wall], 
      [5, 2, wall], 
      [3, 3, wall], 
      [0, 4, wall], 
      [5, 4, wall]
    ],
    "title" : [
      [14, "get forked"]
    ],
    "text" : [
      [13, "this one's"],
      [14, "tricky, retrace"],
      [15, "your steps"],
    ],
    "music" : 2
  },
  // Sprout (L4)
  {
    "map" : map`
111111
111101
111111
111111
011111
11110g
mmmmmm`,
    "add" : [ 
      [2, 3, player],
      [0, 0, wall],
      [0, 3, wall],
      [0, 4, wall],
      [0, 5, wall],
      [1, 1, wall],
      [1, 4, wall],
      [3, 1, wall],
      [4, 1, wall],
      [4, 2, wall],
      [4, 5, wall]
    ],
    "title" : [
      [14, "sprout"]
    ],
    "text" : [
      [13, "think about"],
      [14, "forbidden"],
      [15, "squares"],
    ],
    "music" : 3
  },
];

initialize(level)

function initialize(levelIndex) {
  levelData = levels[levelIndex];
  setMap(levelData["map"]);
  
  if (levelData["add"] != null) {
    for (let i = 0; i < levelData["add"].length; i++) {
      addSprite(levelData["add"][i][0], levelData["add"][i][1], levelData["add"][i][2])
    }
  }
  
  switchSongs(levelData["music"]);
  drawText("title")
}

function switchSongs(to) {
  if (track == to) {
    return;
  }

  track = to;
  playback.end();
  playback = playTune(songs[to], Infinity);
}

function drawText(str) {
  clearText();
  let text = levelData[str];
  if (text != null) {
    for (let i = 0; i < text.length; i++) {
      addText(text[i][1], {y: text[i][0], color: color`4`})
    }
  }
}

onInput("w", () => {
  let p = getFirst(player);
  if (p != null) {
    drawAt(p)
    p.y -= 1
  }
});
onInput("a", () => {
  let p = getFirst(player);
  if (p != null) {
    drawAt(p)
    p.x -= 1
  }
});
onInput("s", () => {
  let p = getFirst(player);
  if (p != null) {
    drawAt(p)
    p.y += 1
  }
});
onInput("d", () => {
  let p = getFirst(player);
  if (p != null) {
    drawAt(p)
    p.x += 1
  }
});
onInput("j", () => {
  reset = true;
});

function drawAt(p) {
  addSprite(p.x, p.y, filled);
}

function isValid() {
  // we check each 2x2 grid to see if either of the two exist:
  // xx || xy
  // xx || yx
  for (let y = 0; y < height()-2; y++) {
    for (let x = 0; x < width()-1; x++) {
      let tl = isFilled(x, y);
      let tr = isFilled(x+1, y);
      if (tl == isFilled(x+1, y+1) && tr == isFilled(x, y+1)) {
        addSprite(x, y, error);
        addSprite(x+1, y, error);
        addSprite(x+1, y+1, error);
        addSprite(x, y+1, error);
        return [false, (tl == tr ? "path too thick" : "diagonal") ]
      }
    }
  }
  return [true, "good job!"]
}

function isFilled(x, y) {
  let t = getTile(x, y);
  for (let i = 0; i < t.length; i++) {
    let tp = t[i].type;
    if (tp == filled || tp == player) {
      return true
    }
  }
  return false
}

afterInput(() => {
  if (level <= tutorial || reset) {
    if (level <= tutorial) {
      level++;
    }
    reset = false;
    initialize(level);
    return;
  }

  let p = getFirst(player);
  let g = getFirst(goal);
  if (p.x == g.x && p.y == g.y) {
    reset = true;
    let valid = isValid();
    if (valid[0]) {
      clearText();
      
      if (level >= levels.length - 1) {
        addText("thanks for", {y: 7, color: color`4`})
        addText("playing! press", {y: 8, color: color`4`})
        addText("any to restart", {y: 9, color: color`4`})
        level = -1;
        return;
      }
      
      addText("good job!", {y: 7, color: color`4`})
      addText("press anything", {y: 8, color: color`4`})
      addText("to continue", {y: 9, color: color`4`})
      level++;
      
    } else {
      drawText("text");
      addText(valid[1], {y: 7, color: color`F`});
    }
  }
});
